import aiomysql
import tornado.ioloop
import tornado.platform.asyncio
import tornado.web
from tornado.options import define, options

import status

define('port', default='8000', type=int, help='run at given port')


class BaseHandler(tornado.web.RequestHandler):
    @property
    def conn(self):
        return self.settings["mysql_conn"]

    def prepare(self):
        self.request_data = {}
        is_json = self.request.headers.get("Content-Type", "").startswith(
            "application/json"
        )
        if self.request.body and is_json:
            self.request_data = tornado.escape.json_decode(self.request.body)

    def set_default_headers(self):
        self.set_header("Content-Type", 'application/json; charset="utf-8"')


class PostListHandler(BaseHandler):
    async def get(self):
        async with self.conn.cursor(aiomysql.DictCursor) as cursor:
            await cursor.execute('SELECT id, title, content FROM posts;')
            rows = await cursor.fetchall()
            self.write({'status': 0, 'results': rows})

    async def post(self):
        title = self.request_data.get('title')
        content = self.request_data.get('content')
        async with self.conn.cursor() as cursor:
            await cursor.execute('''
INSERT INTO posts (title, content) VALUES (%s, %s)
            ''', (title, content))
            # 手动提交，或者在aiomysql.connect方法中设置关键字参数autocommit=True
            await self.conn.commit()
            self.set_status(status.HTTP_201_CREATED)
            self.write({'status': 0})


class PostDetailHandler(BaseHandler):
    async def get(self, post_id):
        async with self.conn.cursor(aiomysql.DictCursor) as cursor:
            await cursor.execute('''
SELECT id, title, content FROM posts WHERE id = %s;
            ''', post_id)
            if cursor.rowcount == 0:
                raise tornado.web.HTTPError(status.HTTP_404_NOT_FOUND)
            post = await cursor.fetchone()
            self.write({'status': 0, 'result': post})

    async def patch(self, post_id):
        title = self.request_data.get('title')
        content = self.request_data.get('content')
        if title is None and content is None:
            raise tornado.web.HTTPError(status.HTTP_400_BAD_REQUEST)

        if title and content:
            sql = 'UPDATE posts SET title = %s, content = %s WHERE id = %s;'
            params = (title, content, post_id)
        # 基于前面判断如果title为None,content一定不为None
        if title is None:
            sql = 'UPDATE posts SET content = %s WHERE id = %s;'
            params = (content, post_id)
        if content is None:
            sql = 'UPDATE posts SET title = %s WHERE id = %s;'
            params = (title, post_id)

        async with self.conn.cursor() as cursor:
            await cursor.execute(sql, params)
            # 如果id不存在会抛HTTP404
            if cursor.rowcount == 0:
                raise tornado.web.HTTPError(status.HTTP_404_NOT_FOUND)

            await self.conn.commit()
            self.write({'status': 0})

    async def delete(self, post_id):
        async with self.conn.cursor() as cursor:
            await cursor.execute('DELETE FROM posts WHERE id = %s', post_id)
            # 如果id不存在会抛HTTP404
            if cursor.rowcount == 0:
                raise tornado.web.HTTPError(status.HTTP_404_NOT_FOUND)
            await self.conn.commit()
            self.write({'status': 0})


def make_app(settings):
    return tornado.web.Application([
        (r'/api/v1/posts/', PostListHandler),
        (r'/api/v1/posts/(\w+)/', PostDetailHandler),
    ], **settings)


# https://github.com/aio-libs/aiomysql
async def connect_mysql():
    return await aiomysql.connect(
        host='127.0.0.1',
        port=3306,
        db='tornado_db',
        user='root',
        password='root',
    )


def main():
    tornado.options.parse_command_line()

    tornado.platform.asyncio.AsyncIOMainLoop().install()
    ioloop = tornado.ioloop.IOLoop.current()

    mysql_conn = ioloop.run_sync(connect_mysql)

    app = make_app(({
        'debug': True,
        'mysql_conn': mysql_conn,
    }))
    app.listen(options.port)
    ioloop.start()


if __name__ == '__main__':
    main()
